import { defineStore } from 'pinia';
import usePlayerConfigStore from './usePlayerConfigStore';
import * as $audio from './audio';
import * as utilString from '@/utils/string';

const Sound_Type_OSC = 'osc';
const Sound_Type_Custom = 'custom';

const oscCommonFreq = [
	196, 220, 247, 267, 294, 327, 
	349, 392, 440, 494, 523, 587,
	659, 698, 784, 880, 988, 1046,
	1175, 1319, 1397, 1568, 1760, 1976
];
const oscWholeFreq = [
	28, 29, 31,
	33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62,
	65, 69, 73, 78, 82, 88, 93, 95, 104, 110, 117, 124,
	131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
	267, 277, 294, 311, 327, 349, 370, 392, 415, 440, 466, 494,
	523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
	1046, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976,
	2093, 2218, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
	4186
]

function createOscSound(name, freq) {
	return {
		st: Sound_Type_OSC,
		id: utilString.getRandomString(),
		name: name || `振荡音 - ${freq}`,
		freq,
		modify: !oscCommonFreq.includes(freq)
	}
}

function createCustomSound(name, buffer) {
	return {
		st: Sound_Type_Custom,
		id: utilString.getRandomString(),
		name,
		buffer,
		modify: true
	}
}

const useStore = defineStore('acfSound', {
	state: () => {
		return {
			oscSounds: oscCommonFreq.map(freq => createOscSound(null, freq)),
			defaultOscSound: createOscSound(null, 440),
			customSounds: [],
			lastSound: null,
			record: new Set()
		}
	},
	getters: {
		allSounds: (state) => {
			return [...state.oscSounds, ...state.customSounds];
		}
	},
	actions: {
		addOscSound(name, freq) {
			const index = this.oscSounds.findIndex(sound => sound.freq >= freq);
			if (index < 0) {
				this.oscSounds.push(createOscSound(name, freq));
			} else {
				this.oscSounds.splice(index, 0, createOscSound(name, freq));
			}
		},
		deleteOscSound(id) {
			this.oscSounds = this.oscSounds.filter(sound => sound.id != id);
		},
		addCustomSound(name, buffer) {
			this.customSounds.push(createCustomSound(name, buffer));
		},
		deleteCustomSound(id) {
			this.customSounds = this.customSounds.filter(sound => sound.id != id);
		},
		vocalSound(sound, onEnd, stopLast) {
			if (!sound) return;
			if (sound.st === Sound_Type_OSC) {
				return this.vocalOscSound(sound.freq, onEnd, stopLast);
			} else if (sound.st === Sound_Type_Custom) {
				return this.vocalCustomSound(sound.buffer, onEnd, stopLast);
			}
		},
		vocalOscSound(freq, onEnd, stopLast) {
			if (stopLast && this.lastSound instanceof AudioNode) {
				this.lastSound.stop();
			}
			const playerConfigStore = usePlayerConfigStore();
			const g = $audio.context.createGain();
			g.gain.value = 1;
			const osc = $audio.context.createOscillator();
			osc.type = 'sine';
			osc.frequency.value = freq;
			osc.addEventListener('ended', () => {
				g.disconnect();
				osc.disconnect();
				this.record.delete(osc);
				onEnd?.();
			})
			osc.connect(g);
			g.connect($audio.context.receiver);
			osc.start();
			g.gain.linearRampToValueAtTime(.5, $audio.context.currentTime + 0.05);
			osc.stop($audio.context.currentTime + 1);
			g.gain.exponentialRampToValueAtTime(0.01, $audio.context.currentTime + 1);
			this.lastSound = osc;
			this.record.add(osc);
			return osc;
		},
		vocalCustomSound(buffer, onEnd, stopLast) {
			if (stopLast && this.lastSound instanceof AudioNode) {
				this.lastSound.stop();
			}
			const playerConfigStore = usePlayerConfigStore();
			const g = $audio.context.createGain();
			g.gain.value = 1;
			const source = $audio.context.createBufferSource();
			source.buffer = buffer;
			source.addEventListener('ended', () => {
				g.disconnect();
				source.disconnect();
				this.record.delete(source);
				onEnd?.();
			})
			source.connect(g);
			g.connect($audio.context.receiver);
			source.start();
			this.lastSoundNode = source;
			this.record.add(source);
			return source;
		},
		clearAllSound() {
			this.record.forEach(node => node.stop());
			this.record.clear();
		}
	}
})

export default useStore;